延續前一篇,此篇針對全局異常處理進一步說明。因為在程式規模變大之際,異常處理會隨之複雜,若是在各自的控制器處理錯誤,會使得程式碼越來越冗贅且難以維護。透過Spring的@ControllerAdvice以及@ExceptionHandler可以更好將異常處理集中管理,提高維護性及可讀性。
@ControllerAdvice是Spring提供的功能,定義全局異常處理器,可以自動攔截所有API控制器的異常。通常與@ExceptionHandler一起使用,可以指定處理特定異常錯誤類型。
@ExceptionHandler是用來指定處理特定異常,可與@ControllerAdvice搭配用於全局控制,或是單獨用於一個控制器當中。
// 建立自定義異常
@ResponseStatus(HttpStatus.BAD_REQUEST)
public class OrderBadRequestException extends RuntimeException {
public OrderBadRequestException(String message) {
super(message);
}
}
// 定義全局異常控制器
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(OrderBadRequestException.class) // 自定義的特定異常處理
public ResponseEntity<Object> handleOrderBadRequest(OrderBadRequestException ex) {
Map<String, Object> body = new HashMap<>();
body.put("status", HttpStatus.BAD_REQUEST.value());
body.put("error", "Bad Request");
body.put("message", ex.getMessage());
return new ResponseEntity<>(body, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(Exception.class) // 通用異常處理
public ResponseEntity<Object> handleGlobalException (Exception ex) {
Map<String, Object> body = new HashMap<>();
body.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
body.put("error", "Internal Server Error");
body.put("message", "wrong");
return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@RestController
public class MemberController {
@GetMapping("/member")
public String member() {
throw new MemberBadRequestException("This is a bad request");
}
@ExceptionHandler(MemberBadRequestException.class)
public ResponseEntity<Object> handleMemberBadRequest(MemberBadRequestException ex) {
Map<String, Object> body = new HashMap<>();
body.put("error", "Bad Request");
body.put("message", ex.getMessage());
return new ResponseEntity<>(body, HttpStatus.BAD_REQUEST);
}
}
// 只處理 com.num.controller 底下的控制器
@ControllerAdvice(basePackages = "com.num.controller")
public class PackageSpecificExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<Object> handleGlobalException(Exception ex) {
Map<String, Object> body = new HashMap<>();
body.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
body.put("error", "Internal Server Error");
body.put("message", ex.getMessage());
return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
// 只處理 MyController 和 AnotherController 異常
@ControllerAdvice(assignableTypes = {MyController.class, AnotherController.class})
public class SpecificControllerExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<Object> handleGlobalException(Exception ex) {
Map<String, Object> body = new HashMap<>();
body.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
body.put("error", "Internal Server Error");
body.put("message", ex.getMessage());
return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
綜合上述,善用全局異常處理,可以避免程式碼重寫,並且增加可讀性,提升維護性,更重要的是,也能更好與前端協作。